/*
 * @Author: lduoduo
 * @Date: 2021-04-09 12:52:52
 * @Last Modified by: lduoduo
 * @Last Modified time: 2021-04-22 17:51:53
 * https://github.com/bpmn-io/diagram-js/blob/master/lib/features/modeling/Modeling.js
    'shape.append': AppendShapeHandler,
    'shape.create': CreateShapeHandler,
    'shape.delete': DeleteShapeHandler,
    'shape.move': MoveShapeHandler,
    'shape.resize': ResizeShapeHandler,
    'shape.replace': ReplaceShapeHandler,
    'shape.toggleCollapse': ToggleShapeCollapseHandler,

    'spaceTool': SpaceToolHandler,

    'label.create': CreateLabelHandler,

    'connection.create': CreateConnectionHandler,
    'connection.delete': DeleteConnectionHandler,
    'connection.move': MoveConnectionHandler,
    'connection.layout': LayoutConnectionHandler,

    'connection.updateWaypoints': UpdateWaypointsHandler,

    'connection.reconnect': ReconnectConnectionHandler,

    'elements.create': CreateElementsHandler,
    'elements.move': MoveElementsHandler,
    'elements.delete': DeleteElementsHandler,

    'elements.distribute': DistributeElementsHandler,
    'elements.align': AlignElementsHandler,

    'element.updateAttachment': UpdateAttachmentHandler
 * 事件机制
 * 这里使用了EventBus进行订阅发布用于属性面板和modeler的数据通信
 * EventBus.emit('element-data', data) -> modeler推送节点数据到属性面板
 * EventBus.on('post-data', data) -> modeler收到来自属性面板的数据，提交更新到modeler
 * EventBus.on('sync-data', data) -> modeler收到来自属性面板的同步数据请求，根据元素id获取对应的数据进行推送
 */
import { debounce } from 'lodash';
import { message } from 'antd';

import EventBus from './events';

const Cache = {
  lastShape: null,
};

// 修改属性
export const updateProperties = (modeler) => (element, properties) => {
  const { modeling } = modeler;

  try {
    modeling.updateProperties(element, properties);
  } catch (e) {
    // console.log('err', e)
    message.error(e.message);
  }
};

// 监听 modeler, 目前用不到
export const initModelerEvents = (modeler) => {
  const elementRegistry = modeler.elementRegistry;
  // 这里我是用了一个forEach给modeler上添加要绑定的事件
  const events = ['shape.added', 'shape.move.end', 'shape.removed', 'connect.end', 'connect.move'];

  modeler.on('commandStack.changed', (e) => {
    console.log('MODELER commandStack.changed', e);
  });

  modeler.on('comments.updated', (e) => {
    console.log('MODELER comments.updated', e);
  });

  modeler.on('canvas.click', function () {
    modeler.get('comments').collapseAll();
  });

  events.forEach((event) => {
    modeler.on(event, (e) => {
      console.log('MODELER Events', event, e);
      var shape = e.element ? elementRegistry.get(e.element.id) : e.shape;
      console.log('MODELER Events shape', shape);
    });
  });
};

// 流程图有任何变化，立即反应到属性面板
const doSendData = debounce((shape) => {
  EventBus.emit('element-data', shape.businessObject, shape);
}, 300);

// 收到来自属性面板的数据，提交更新
const onReceiveData = (modeler) => (data, shape) => {
  const { elementRegistry } = modeler;
  // const shape = elementRegistry.get(e.id);

  updateProperties(modeler)(shape, data);
  // updateProperties(modeler)(shape, e);
};

// 收到来自属性面板的同步请求，发送数据
const onSyncData = (modeler) => (id) => {
  const { elementRegistry } = modeler;
  let shape = elementRegistry.get(id);

  if (shape) {
    return EventBus.emit('element-data', shape.businessObject);
  }

  if (id === 'root') {
    // 方式一：比较笨
    // const p = Object.values(elementRegistry._elements).find(
    //   (d) => d.element && d.element.parent === undefined
    // );
    // const shape = p.element;

    // 方式二
    // const p = modeler.getDefinitions().rootElements[0];
    const p = modeler.get('canvas').getRootElement();

    shape = elementRegistry.get(p.id);
    shape && EventBus.emit('element-data', shape.businessObject, shape);
  }
};

const onEventClick = debounce((type, element, elementRegistry) => {
  console.log('ELEMENT events type', type, element);

  // if (!e || (e.element && e.element.type == 'bpmn:Process')) return; // 这里根元素是bpmn:Process

  if (/(click|added)/.test(type)) {
    const shape = elementRegistry.get(element.id);

    Cache.lastShape = shape;

    // EventBus.emit('content-pad-show', shape, e);

    return doSendData(shape);
  }

  if (/changed/.test(type)) {
    const shape = elementRegistry.get(element.id);

    shape && Cache.lastShape?.id === shape.id && doSendData(shape);
  }
});

// 监听 element
export const initEventBus = (modeler) => {
  const { elementRegistry } = modeler; // 可以使用elementRegistry来获取Shape信息

  EventBus.emit('ready', modeler);

  // 注册监听事件，来自属性面板的数据推送
  EventBus.on('post-data', onReceiveData(modeler));
  EventBus.on('sync-data', onSyncData(modeler));

  const eventBus = modeler.get('eventBus');

  // you may hook into any of the following events
  const events = [
    // 'element.hover',
    // 'element.out',
    'element.click',
    'element.dblclick',
    'element.changed',
    // 'element.mousedown',
    // 'element.mouseup',
    'shape.added',
    'shape.move.end',
    'shape.removed',
    'connect.end',
    'connect.move',
  ];

  events.forEach(function (event) {
    eventBus.on(event, (e) => onEventClick(e.type, e.element, elementRegistry));
  });

  // Option 2:
  // directly attach an event listener to an elements graphical representation
  // each model element a data-element-id attribute attached to it in HTML
  // select the end event
  // var endEventNode = document.querySelector('#canvas [data-element-id=END_PROCESS]');
  // endEventNode &&
  //   endEventNode.addEventListener('click', function (e) {
  //     alert('clicked the end event!');
  //   });
};

// 添加覆盖物
export const initOverlays = (modeler) => {
  var canvas = modeler.get('canvas');
  var overlays = modeler.get('overlays');

  canvas.zoom('fit-viewport');

  // attach an overlay to a node
  overlays.add('SCAN_OK', 'note', {
    position: {
      bottom: 0,
      right: 0,
    },
    html: '<div class="diagram-note">Mixed up the labels?</div>',
  });

  // configure scale=false to use non-scaling overlays
  overlays.add('START_PROCESS', 'note', {
    position: {
      bottom: 0,
      right: 0,
    },
    scale: false,
    html: '<div class="diagram-note">I don\'t scale</div>',
  });

  // configure scale={ min: 1 } to use non-shrinking overlays
  overlays.add('SCAN_QR_CODE', 'note', {
    position: {
      bottom: 0,
      right: 0,
    },
    scale: { min: 1 },
    html: '<div class="diagram-note">I don\'t shrink beyond 100%</div>',
  });

  // configure show={ minZoom: 0.6 } to hide overlays at low zoom levels
  overlays.add('END_PROCESS', 'note', {
    position: {
      bottom: 0,
      right: 0,
    },
    show: {
      minZoom: 0.7,
    },
    html: '<div class="diagram-note">I hide at low zoom levels</div>',
  });
};
